package utils

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"
)

// CheckDir 检查目录是否存在
func CheckDir(path string) bool {
	f, err := os.Stat(path)
	if os.IsNotExist(err) {
		Logger.Debug(fmt.Sprintf("dir %s not exists", path))
		return false
	}
	Logger.Debug(fmt.Sprintf("dir %s exists，perm:%d", path, f.Mode().Perm()))
	fmt.Println()
	return true
}

// CreateDir 创建目录
func CreateDir(path string, perm os.FileMode) error {
	Logger.Debug(fmt.Sprintf("create dir %s ,perm: %d", path, perm.Perm()))
	err := os.MkdirAll(path, perm)
	if err != nil {
		Logger.Errorf(fmt.Sprintf("failed to create dir  %s ,error: %s", path, err))
		return err
	}
	Logger.Info(fmt.Sprintf("successed to create dir %s", path))
	return nil
}

// CreateDirIfNotExist 目录不存在则创建目录，否则不作处理
func CreateDirIfNotExist(path string, perm os.FileMode) error {
	check := CheckDir(path)
	if check == false {
		err := CreateDir(path, perm)
		if err != nil {
			return err
		}
	}
	return nil
}

// OpenFile 打开文件，不存在则创建
func OpenFile(path string, flag int, perm os.FileMode) (*os.File, error) {
	dirPath, fileName := filepath.Split(path)
	if fileName == "" {
		errStr := "file name is empty"
		Logger.Error(errStr)
		return nil, fmt.Errorf(errStr)
	}
	err := CreateDirIfNotExist(dirPath, perm)
	if err != nil {
		return nil, err
	}
	file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|flag, perm)
	if err != nil {
		return nil, err
	}

	return file, nil
}

// WriteStringToFile 字符串写入文件，文件不存在则创建
func WriteStringToFile(path string, content string, flag int, perm os.FileMode) error {
	Logger.Debug(fmt.Sprintf("write file %s", path))

	file, err := OpenFile(path, flag, perm)
	if err != nil {
		return err
	}
	defer file.Close()

	// 将字符串写入文件
	_, err = file.WriteString(content)
	if err != nil {
		Logger.Error(err)
		return err
	}

	Logger.Info(fmt.Sprintf("write file %s success", path))

	return nil

}

// RemoveAll rm -rf path
func RemoveAll(path string) error {
	err := os.RemoveAll(path)
	if err != nil {
		Logger.Error(err)
		return err
	}

	Logger.Info(fmt.Sprintf("%s has been removed", path))
	return nil
}

// DownloadFile 下载文件到指定位置
func DownloadFile(url string, path string, perm os.FileMode, override bool) error {
	_, err := os.Stat(path)
	needDeleteFlag := false
	if err == nil {
		//文件存在
		Logger.Debug(fmt.Sprintf("%s is exists", path))
		if override {
			// 覆盖原文件后执行下载逻辑
			Logger.Warning(fmt.Sprintf("download %s and override %s", url, path))
			needDeleteFlag = true

		} else {
			// 不覆盖，直接返回
			Logger.Warning(fmt.Sprintf("%s exists,download %s terminated", path, url))
			return nil
		}
	}
	t1 := time.Now()

	res, err := http.Get(url)
	Logger.Debug(fmt.Sprintf("http get %s", url))
	if err != nil {
		Logger.Error(err)
		return err
	}
	defer res.Body.Close()
	Logger.Debug(fmt.Sprintf("http response, code %d", res.StatusCode))
	if res.StatusCode != http.StatusOK {
		e := fmt.Sprintf("call %s status code %d", url, res.StatusCode)
		Logger.Error(e)
		return fmt.Errorf(e)
	}

	if needDeleteFlag {
		err = RemoveAll(path)
		if err != nil {
			return err
		}
	}
	file, err := OpenFile(path, os.O_TRUNC, perm)
	if err != nil {
		return err
	}

	_, err = io.Copy(file, res.Body)
	if err != nil {
		Logger.Error(err)
		return nil
	}
	t2 := time.Now()

	Logger.Info(fmt.Sprintf("download from %s finished, duration: %s", url, t2.Sub(t1).String()))

	return nil
}

// GetDirDetailList 获取目录下所有含prefix前缀的目录及文件
func GetDirDetailList(dirPath string, prefix string) ([]os.DirEntry, error) {
	Logger.Debug(fmt.Sprintf("get dir %s with prefix %s", dirPath, prefix))
	entryList, err := os.ReadDir(dirPath)
	if err != nil {
		return []os.DirEntry{}, err
	}
	result := []os.DirEntry{}
	for _, entry := range entryList {
		if strings.HasPrefix(entry.Name(), prefix) {
			Logger.Debug(fmt.Sprintf("%s", entry.Name()))
			result = append(result, entry)
		}
	}
	return result, nil
}

func CalcMd5(filePath string) (string, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return "", err
	}
	defer file.Close()

	hasher := md5.New()
	if _, err := io.Copy(hasher, file); err != nil {
		return "", err
	}

	return hex.EncodeToString(hasher.Sum(nil)), nil
}
